<template>
  <div class="upload-container">
    <h1 class="upload-title">文件上传</h1>
    <div class="upload-controls">
      <input type="file" @change="handleFileChange" class="file-input" />
      <div class="button-group">
        <button @click="startUpload" class="btn btn-upload" :disabled="!file">上传</button>
        <button @click="pauseUpload" v-if="!isPaused" class="btn btn-pause">暂停</button>
        <button @click="resumeUpload" v-if="isPaused" class="btn btn-resume">继续</button>
      </div>
    </div>
    <div class="progress-area" v-if="showProgress">
      <div class="progress-bar-container">
        <div class="progress-bar" :style="{ width: progress + '%' }">
          <span class="progress-text">{{ progress.toFixed(2) }}%</span>
        </div>
      </div>
      <div class="upload-info">
        <div class="upload-status" v-html="uploadStatus"></div>
        <div class="upload-speed">{{ uploadSpeed }}</div>
        <div class="estimated-time">{{ estimatedTime }}</div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onUnmounted } from "vue";
import axios from "axios";

// 每个文件块的大小，设置为 8MB
const CHUNK_SIZE = 8 * 1024 * 1024;

// 定义响应式变量
const file = ref(null);
const totalChunks = ref(0);
const uploadedChunks = ref(0);
const uploadStartTime = ref(0);
const isPaused = ref(false);
const uploadQueue = ref([]);
const currentUploads = ref(0);
const maxConcurrentUploads = ref(3);
const uploadedBytes = ref(0);
const totalBytes = ref(0);
const lastUploadedBytes = ref(0);
const speedInterval = ref(null);
const progress = ref(0);
const uploadStatus = ref("准备上传...");
const uploadSpeed = ref("");
const estimatedTime = ref("");
const showProgress = ref(false);

// 处理文件选择事件
const handleFileChange = event => {
  const selectedFile = event.target.files[0];
  if (!selectedFile) return;
  // 检查是否选择了相同的文件且正在上传
  if (file.value && file.value.name === selectedFile.name && showProgress.value) {
    alert("该文件正在上传中，请等待上传完成或暂停上传后再重新选择。");
    return;
  }
  file.value = selectedFile;
  resetUpload();
};

// 重置上传状态
const resetUpload = () => {
  clearInterval(speedInterval.value);
  totalChunks.value = 0;
  uploadedChunks.value = 0;
  uploadStartTime.value = Date.now();
  uploadedBytes.value = 0;
  lastUploadedBytes.value = 0;
  isPaused.value = false;
  uploadQueue.value = [];
  currentUploads.value = 0;
  progress.value = 0;
  uploadStatus.value = "准备上传...";
  uploadSpeed.value = "";
  estimatedTime.value = "";
  showProgress.value = true;
};

// 计算上传速度和预计剩余时间
const calculateSpeed = () => {
  const bytesIncrease = uploadedBytes.value - lastUploadedBytes.value;
  const speed = bytesIncrease / 1024 / 1024; // MB/s
  lastUploadedBytes.value = uploadedBytes.value;

  const remainingBytes = totalBytes.value - uploadedBytes.value;
  const timeRemaining = speed > 0 ? remainingBytes / (speed * 1024 * 1024) : 0;

  uploadSpeed.value = `上传速度: ${speed.toFixed(2)} MB/s`;
  estimatedTime.value = `预计剩余时间: ${Math.round(timeRemaining)}秒`;
};

// 开始上传文件
const startUpload = async () => {
  if (!file.value) {
    alert("请选择文件！");
    return;
  }

  totalBytes.value = file.value.size;

  const fileKey = `upload_${file.value.name}`;
  const savedProgress = localStorage.getItem(fileKey);
  if (savedProgress) {
    const resume = confirm("检测到未完成的上传，是否继续上传？");
    if (resume) {
      const progressData = JSON.parse(savedProgress);
      uploadedChunks.value = progressData.uploadedChunks;
      uploadedBytes.value = progressData.uploadedBytes;
    } else {
      // 清除本地存储的进度信息
      localStorage.removeItem(fileKey);
      resetUpload();
    }
  }

  speedInterval.value = setInterval(calculateSpeed, 1000);
  createFileChunks();
};

// 创建文件块
const createFileChunks = () => {
  let fileStreamPos = 0;
  let chunkIndex = 0;
  uploadQueue.value = [];

  while (fileStreamPos < file.value.size) {
    const chunkEnd = Math.min(fileStreamPos + CHUNK_SIZE, file.value.size);
    const chunk = file.value.slice(fileStreamPos, chunkEnd);
    const partNumber = chunkIndex + 1;

    const chunkInfo = {
      file: chunk,
      fileName: file.value.name,
      partNumber,
      start: fileStreamPos,
      end: chunkEnd,
      size: chunk.size,
      totalSize: file.value.size
    };

    if (!isChunkUploaded(partNumber)) {
      uploadQueue.value.push(chunkInfo);
    }

    fileStreamPos = chunkEnd;
    chunkIndex++;
  }

  totalChunks.value = chunkIndex;
  if (totalChunks.value === 0) {
    // 处理文件大小为 0 的情况
    progress.value = 100;
    uploadStatus.value = "✓ 上传成功！";
    clearInterval(speedInterval.value);
    localStorage.removeItem(`upload_${file.value.name}`);
    alert("文件上传成功！");
    resetUpload();
    return;
  }
  uploadStatus.value = `开始上传：共 ${totalChunks.value} 个分片`;
  processQueue();
};

// 检查文件块是否已上传
const isChunkUploaded = partNumber => {
  const fileKey = `upload_${file.value.name}`;
  const savedProgress = localStorage.getItem(fileKey);
  if (!savedProgress) return false;

  const progressData = JSON.parse(savedProgress);
  return progressData.uploadedChunks.includes(partNumber - 1);
};

// 处理上传队列
const processQueue = () => {
  if (isPaused.value) return;
  while (currentUploads.value < maxConcurrentUploads.value && uploadQueue.value.length > 0) {
    const chunk = uploadQueue.value.shift();
    uploadChunk(chunk);
    currentUploads.value++;
  }
};

// 上传单个文件块
const uploadChunk = async chunk => {
  const formData = new FormData();
  const filePartName = `${chunk.fileName}.partNumber-${chunk.partNumber}`;
  formData.append("file", chunk.file, filePartName);

  const urlParams = new URLSearchParams({
    partNumber: chunk.partNumber,
    chunks: totalChunks.value,
    size: chunk.size,
    start: chunk.start,
    end: chunk.end,
    total: chunk.totalSize,
    fileName: chunk.fileName
  });

  try {
    await axios.post(`http://localhost:8089/api/upload/upload?${urlParams.toString()}`, formData, {
      headers: { "Content-Type": "multipart/form-data" },
      onUploadProgress: e => {
        if (e.lengthComputable) {
          const currentChunkProgress = e.loaded;
          // 计算当前总上传字节数
          const totalProgress = uploadedBytes.value - chunk.size + currentChunkProgress;
          progress.value = (totalProgress / totalBytes.value) * 100;
          uploadedBytes.value = totalProgress;
          updateProgress();
        }
      }
    });

    // 分片上传完成后，累加分片的总字节数
    uploadedBytes.value += chunk.size;
    uploadedChunks.value++;
    currentUploads.value--;

    saveProgress();
    updateProgress();

    if (uploadedChunks.value === totalChunks.value) {
      onUploadComplete();
    } else {
      processQueue();
    }
  } catch (error) {
    console.error("上传失败:", error);
    currentUploads.value--;
    uploadQueue.value.push(chunk);
    uploadStatus.value = `<span style="color: red;">分片 ${chunk.partNumber} 上传失败，准备重试</span>`;
    setTimeout(processQueue, 3000);
  }
};

// 保存上传进度到本地存储
const saveProgress = () => {
  const fileKey = `upload_${file.value.name}`;
  const progressData = {
    uploadedChunks: uploadedChunks.value,
    uploadedBytes: uploadedBytes.value,
    totalChunks: totalChunks.value,
    timestamp: Date.now()
  };
  localStorage.setItem(fileKey, JSON.stringify(progressData));
};

// 更新上传进度
const updateProgress = () => {
  progress.value = (uploadedBytes.value / totalBytes.value) * 100;

  const timeElapsed = Math.round((Date.now() - uploadStartTime.value) / 1000);
  uploadStatus.value = `已完成: ${uploadedChunks.value}/${totalChunks.value} 分片，已用时：${timeElapsed}秒`;
};

// 上传完成处理
const onUploadComplete = () => {
  clearInterval(speedInterval.value);
  const fileKey = `upload_${file.value.name}`;
  localStorage.removeItem(fileKey);
  uploadStatus.value = "✓ 上传成功！";
  isPaused.value = false;
  alert("文件上传成功！");
  resetUpload();
};

// 暂停上传
const pauseUpload = () => {
  isPaused.value = true;
  uploadStatus.value = "上传已暂停";
};

// 继续上传
const resumeUpload = () => {
  isPaused.value = false;
  uploadStatus.value = "继续上传...";
  processQueue();
};

// 组件卸载时清除定时器
onUnmounted(() => {
  clearInterval(speedInterval.value);
});
</script>

<style scoped>
.upload-container {
  max-width: 600px;
  padding: 20px;
  margin: 2rem auto;
  background: #f5f5f5;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgb(0 0 0 / 10%);
}
.upload-title {
  color: #333333;
  text-align: center;
}
.upload-controls {
  margin: 20px 0;
}
.file-input {
  display: block;
  width: 100%;
  padding: 8px;
  margin: 10px 0;
}
.button-group {
  display: flex;
  gap: 10px;
  margin-top: 15px;
}
.btn {
  padding: 8px 16px;
  cursor: pointer;
  border: none;
  border-radius: 4px;
  transition: opacity 0.3s;
}
.btn:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}
.btn-upload {
  color: white;
  background: #4caf50;
}
.btn-pause {
  color: white;
  background: #ff9800;
}
.btn-resume {
  color: white;
  background: #2196f3;
}
.progress-area {
  margin-top: 20px;
}
.progress-bar-container {
  height: 24px;
  overflow: hidden;
  background: #dddddd;
  border-radius: 12px;
}
.progress-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  background: #4caf50;
  transition: width 0.3s ease;
}
.progress-text {
  font-size: 12px;
  color: white;
}
.upload-info {
  margin-top: 10px;
  font-size: 14px;
}
.upload-status {
  margin: 5px 0;
  color: #666666;
}
.upload-speed,
.estimated-time {
  color: #666666;
}
</style>
